#include<stdint.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<getopt.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<linux/types.h>
#include<linux/spi/spidev.h>
#include<string.h>

#include "spitest.h"

static const char *devices="/dev/spidev0.0";
static uint8_t mode =SPI_MODE_0;//SPI_MODE_2;//SPI_MODE_0;//SPI_MODE_1;/*SPI transfer use double work set POL=0 PHA=0*/
static uint8_t bits=8;//8 bits read or write
uint32_t  speed = 8*1000*1000; //8M rate
static uint16_t delay = 0;
int fd;//devices

data_package_t tmp_package , rtmp_package = {0};

static int package_data (uint8_t *pbuff, int len , unsigned char status) {

    if (pbuff == NULL || len<= 0 || len >= PACKAGE_BODY_SIZE_MAX_BYTE) {
        return -2;
    }
    tmp_package.header[0] = IS_HOST;//M is for master, S for slave
    tmp_package.header[1] = 'O';
    tmp_package.header[2] = (len>>8) & 0xff;//data lenght high 
    tmp_package.header[3] = len & 0xff;  // data lenght low

    memcpy(&tmp_package.body[0], pbuff, len);

    data_fill_crc16(&tmp_package);//data ready to send to spi bus

    if ( status == READ_CMD) {
        tmp_package.dummy[2] = (READ_CMD>>8) & 0xff;
        tmp_package.dummy[3] = READ_CMD & 0xff;
        //仿modbus协议，通信帧头+(功能码)+读写命令+数据长度(2字节)+数据+校验，从can获取
        spi_read_config(&tmp_package, sizeof(tmp_package));
    }

    if ( status == WRITE_CMD) {
        tmp_package.dummy[2] = (WRITE_CMD>>8) & 0xff;
        tmp_package.dummy[3] = WRITE_CMD & 0xff;
        //仿modbus协议，通信帧头+(功能码)+读写命令+数据长度(2字节)+数据+校验，传送给can
        spi_write_config(&tmp_package,sizeof(tmp_package));
    }

    if ( status == WREAD_CMD) {
        //全双工通信需要先把接收端数据池清零
        memset(&rtmp_package, 0 , sizeof(rtmp_package));
        tmp_package.dummy[2] = (WREAD_CMD>>8) & 0xff;
        tmp_package.dummy[3] = WREAD_CMD & 0xff;
        spi_transfer_config(&tmp_package, &rtmp_package, sizeof(tmp_package));
    }

    return 0;
}

static uint16_t Updatecrc16(uint16_t crcin, uint8_t byte) {
    uint32_t crc = crcin;
    uint32_t in = byte | 0x100;
    do {
        crc <<=1; in <<=1;
        if (in & 0x100) ++crc;
        if (crc & 0x10000) crc ^= 0x1021;
    }while(!(in & 0x10000));

    return (crc & 0xffffu);
}

uint16_t cal_crc16(const uint8_t *data, uint32_t size){
    uint32_t crc = 0xffff/*0*/;
    const uint8_t *dataEnd = data + size;

    while(data < dataEnd) {
        crc = Updatecrc16(crc, *data++);
    }

    crc = Updatecrc16(crc, 0);

    crc = Updatecrc16(crc, 0);

    return ( crc & 0xffffu);
}

static void data_fill_crc16(data_package_t *p_data) {
    uint16_t crc16_tx = 0;

    crc16_tx = cal_crc16((uint8_t *)p_data->header, 
        BUFFER_SIZE_BYTE - PACKAGE_CRC16_SIZE_BYTE - PACKAGE_DUMMY_SIZE_BYTE);
    
    p_data->crc16[0] = crc16_tx & 0xff;

    p_data->crc16[1] = (crc16_tx >> 8) & 0xff;

    printf("crc %x, %x\r\n", p_data->crc16[0], p_data->crc16[1]);
}

static int spi_init_config(void) {
    int ret = 0;
    fd = open (devices, O_RDWR);
    if ( fd < 0) printf("can't open devices.\n");
    /*spi mode*/
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1) printf("can't set wr spi mode!\n");
 
	ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
	if (ret == -1) printf("can't get rd spi mode!\n");
 
	/*bits per word*/
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1) printf("can't set bits per word!\n");
 
	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1) printf("can't get bits per word!\n");
 
	/*max speed hz*/
	//ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1) printf("can't get max speed hz!\n");
 
	printf("spi device: %s\n", devices);
	printf("spi mode: %d\n", mode);
	printf("bits per word: %d\n", bits);
	printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

    return ret;
}

static int spi_transfer_config(uint8_t *txpbuff, uint8_t *rxpbuff, int len) {
    int ret = 0;

    struct spi_ioc_transfer tr = {
        .tx_buf = (uint16_t)txpbuff, 
        .rx_buf = (uint16_t)rxpbuff,
        .len = len, 
        .delay_usecs = delay,
        .speed_hz = speed, 
        .bits_per_word = bits
    };
    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

    if (ret <= 0) printf("spi transfer error.\n");
    return ret;
}

static int spi_write_config(uint8_t *tbuff, int len) {
    int ret = 0;
    hex_dump(tbuff, len , len , "tx");
    ret=write(fd, tbuff, len);
    //hex_dump(tbuff, len , len , "tx");
    if(ret<0) printf("spi write error!\n");
    return ret;
}

static int spi_read_config(uint8_t *rbuff, int len) {
    int ret = 0;
    ret=read(fd, rbuff, len);
    hex_dump(rbuff, len , len , "rx");
    if(ret<0) printf("spi read error!\n");
    return ret;
}

static int spi_close_config(void) {
    if ( fd == 0) 
        return 0;
    close(fd);
    fd = 0;
    return 0;
}

static int spi_communication_mode(void)
{
    if (tmp_package.header[0] == IS_HOST)
        return IS_HOST;
    if (tmp_package.header[0] == IS_SLAVE)
        return IS_SLAVE;
    if (tmp_package.header[0] == IS_UNKNOW)
        return IS_UNKNOW;
    else
        return -1;
}

static unsigned int spi_get_func_code(void) 
{
   return (tmp_package.dummy[0]);
}

static void spi_test_poll(void)
{
    unsigned int state = 0; uint8_t p[8] = {0};
    memset(&tmp_package, 0 , sizeof(tmp_package));
    tmp_package.dummy[0] = (CAN_CMD_START_STOP>>8) & 0xff;
    tmp_package.dummy[1] = CAN_CMD_START_STOP & 0xff;

    if(spi_get_func_code() == CAN_CMD_RESET_COUNTERS)
    {
        memset(p, 0x00, sizeof(p));
        package_data(p, sizeof(p), WRITE_CMD);
    }

    if(spi_get_func_code() == CAN_CMD_RESET_SYSTEM)
    {
        memset(p, 0x01, sizeof(p));
        package_data(p, sizeof(p), WRITE_CMD);
    } 

    if(spi_get_func_code() == CAN_CMD_START_STOP)
    {
        if(state == 1)//表示此时打开can状态 
        {
            memset(p, 0x00, sizeof(p));
            package_data(p, 1, WRITE_CMD);//判断是打开状态需要关闭写1字节0x00 
        }else{
            memset(p, 0x01, sizeof(p));
            package_data(p, 1 , WRITE_CMD);//判断是关闭状态需要打开写1字节0x01
        }
    }
    
}

static void hex_dump(const void *src, size_t length, size_t line_size,

             char *prefix)

{

    int i = 0;

    const unsigned char *address = src;

    const unsigned char *line = address;

    unsigned char c;

    printf("%s | ", prefix);

    while (length-- > 0) {

        printf("%02X ", *address++);

        if (!(++i % line_size) || (length == 0 && i % line_size)) {

            if (length == 0) {

                while (i++ % line_size)

                    printf("__ ");

            }

            printf(" |");

            while (line < address) {

                c = *line++;

                printf("%c", (c < 32 || c > 126) ? '.' : c);

            }

            printf("|\n");

            if (length > 0)

                printf("%s | ", prefix);
        }

    }
}

int main()
{
  static unsigned int status = 0;

  status = spi_init_config();
  if ( status == -1 || status == 0xf1)
    printf("spi init failure %d\r,please try again.\n",status);
 
  while(1) 
  {
    spi_test_poll();
  }
  status = spi_close_config();
  if ( status != 0)
    printf("spi close failure %d\r,please try again.\n",status);

  return 0;
}